import esbuild from "esbuild";
import * as sass from "sass";
import path from "path";
import fs from "fs";
import chokidar from "chokidar";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
import settings from "../../settings.local.json";
import postcssImport from "postcss-import";
import postcssBanner from "postcss-banner";

// Define input and output file types
interface FileConfig {
  input: string;
  output: string;
}

const mode = process.argv.includes("--watch") ? "watch" : "build";

// List of files to process
const files: FileConfig[] = [
  {
    input: "src/styles/default-css/10.0.0/default.scss",
    output:
      mode === "build"
      ? path.relative(process.cwd(), path.resolve("../../Website/Resources/Shared/stylesheets/dnndefault/10.0.0/default.css"))
      : path.resolve(settings.WebsitePath, "Resources/Shared/stylesheets/dnndefault/10.0.0/default.css"),
  },
  {
    input: "src/styles/install/install.scss",
    output:
      mode === "build"
      ? path.relative(process.cwd(), path.resolve("../../Website/Install/Install.css"))
      : path.resolve(settings.WebsitePath, "Install/Install.css"),
  }
  // { input: "src/scripts/test.ts", output: "dist/scripts/test.js" },
];

// Helper function to ensure directories exist
function ensureDirectoryExists(filePath: string): void {
  const dir = path.dirname(filePath);
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}

function normalizePath(filePath: string): string {
  return path.resolve(filePath).replace(/\\/g, "/");
}

// Compile SCSS to CSS with sourcemaps
async function buildScss(input: string, output: string): Promise<void> {
  try {
    const result = sass.compile(
        input,
        {
          sourceMap: true,
          sourceMapIncludeSources: true,
        }
    );

    const cssWithBanner = result.css;
    const postcssResult = await postcss([
        postcssImport(),
        autoprefixer,
        cssnano,
        postcssBanner({
          banner: "This file is generated by the Dnn.ClientSide project.\nDo not edit it directly.\nChanges will be overwritten upon upgrades.\n",
          important: true,
        })
    ])
    .process(cssWithBanner, {
        from: normalizePath(input),
        to: normalizePath(output),
        map: {
            inline: false,
            annotation: `${path.basename(output)}.map`,
            prev: result.sourceMap,
            sourcesContent: true,
        },
    });

    ensureDirectoryExists(output);
    console.log(`Writing CSS to: ${output}`);
    fs.writeFileSync(output, postcssResult.css);
    console.log(`CSS written to: ${output}`);

    if (postcssResult.map) {
        const sourcemapPath = `${output}.map`;
        fs.writeFileSync(sourcemapPath, JSON.stringify(postcssResult.map));
        console.log(`Sourcemap written to: ${sourcemapPath}`);
    }
  } catch (error) {
    console.error(`Error compiling SCSS for ${input}:`, error);
  }
}

// Bundle TypeScript/JavaScript with esbuild
async function buildJs(input: string, output: string): Promise<void> {
  try {
    ensureDirectoryExists(output);
    await esbuild.build({
      entryPoints: [input],
      outfile: output,
      bundle: true,
      sourcemap: true,
      format: "iife",
      target: "es2018",
      minify: true,
    });
    console.log(`JS written to: ${output}`);
  } catch (error) {
    console.error(`Error bundling JS for ${input}:`, error);
  }
}

// Process all files
async function buildAll(): Promise<void> {
  for (const { input, output } of files) {
    if (input.endsWith(".scss")) {
      await buildScss(input, output);
    } else if (input.endsWith(".ts") || input.endsWith(".js")) {
      await buildJs(input, output);
    }
  }
}

// Watch for changes (optional)
function watchFiles(): void {
  const watcher = chokidar.watch(
    "./src",
  {
    ignored: /(^|[\/\\])\../, // Ignore dotfiles
    persistent: true,
    ignoreInitial: true,
  });
  buildAll();
  watcher.on("all", async (event, filePath) => {
      await buildAll();
  });
}

// Entry point
const args = process.argv.slice(2);
if (args.includes("--watch")) {
  console.log("Watching for file changes...");
  watchFiles();
} else {
  buildAll();
}
